/*
  AudioGeneratorFLAC
  Audio output generator that plays FLAC audio files
    
  Copyright (C) 2017  Earle F. Philhower, III

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _AUDIOGENERATORFLAC_H
#define _AUDIOGENERATORFLAC_H

#include <AudioGenerator.h>
extern "C" {
    #include "libflac/FLAC/stream_decoder.h"
};

class AudioGeneratorFLAC : public AudioGenerator
{
  public:
    AudioGeneratorFLAC();
    virtual ~AudioGeneratorFLAC() override;
    virtual bool begin(AudioFileSource *source, AudioOutput *output) override;
    virtual bool loop() override;
    virtual bool stop() override;
    virtual bool isRunning() override;

  protected:
    // FLAC info
    uint16_t channels;
    uint32_t sampleRate;
    uint16_t bitsPerSample;

    // We need to buffer some data in-RAM to avoid doing 1000s of small reads
    const int *buff[2];
    uint16_t buffPtr;
    uint16_t buffLen;
    FLAC__StreamDecoder *flac;

    // FLAC callbacks, need static functions to bounce into c++ from c
    static FLAC__StreamDecoderReadStatus _read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) {
      return static_cast<AudioGeneratorFLAC*>(client_data)->read_cb(decoder, buffer, bytes);
    };
    static FLAC__StreamDecoderSeekStatus _seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) {
      return static_cast<AudioGeneratorFLAC*>(client_data)->seek_cb(decoder, absolute_byte_offset);
    };
    static FLAC__StreamDecoderTellStatus _tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) {
      return static_cast<AudioGeneratorFLAC*>(client_data)->tell_cb(decoder, absolute_byte_offset);
    };
    static FLAC__StreamDecoderLengthStatus _length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) {
      return static_cast<AudioGeneratorFLAC*>(client_data)->length_cb(decoder, stream_length);
    };
    static FLAC__bool _eof_cb(const FLAC__StreamDecoder *decoder, void *client_data) {
      return static_cast<AudioGeneratorFLAC*>(client_data)->eof_cb(decoder);
    };
    static FLAC__StreamDecoderWriteStatus _write_cb(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) {
      return static_cast<AudioGeneratorFLAC*>(client_data)->write_cb(decoder, frame, buffer);
    };
    static void _metadata_cb(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) {
      static_cast<AudioGeneratorFLAC*>(client_data)->metadata_cb(decoder, metadata);
    };
    static void _error_cb(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) {
      static_cast<AudioGeneratorFLAC*>(client_data)->error_cb(decoder, status);
    };
    // Actual FLAC callbacks
    FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes);
    FLAC__StreamDecoderSeekStatus seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset);
    FLAC__StreamDecoderTellStatus tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset);
    FLAC__StreamDecoderLengthStatus length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length);
    FLAC__bool eof_cb(const FLAC__StreamDecoder *decoder);
    FLAC__StreamDecoderWriteStatus write_cb(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[]);
    void metadata_cb(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata);
    void error_cb(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status);
};

#endif

